package com.csun.cmny.provider.device.phone;

import com.csun.cmny.provider.device.phone.entity.*;
import com.csun.cmny.provider.model.constant.DmcConstant;
import com.csun.cmny.provider.model.domain.*;
import com.csun.cmny.provider.model.dto.phone.PhoneZoneDto;
import com.csun.cmny.provider.model.enums.device.DeviceStatusEnum;
import com.csun.cmny.provider.model.enums.device.DmcDeviceAlertTypeEnum;
import com.csun.cmny.provider.model.vo.OldmanBedRoomBuildingVo;
import com.csun.cmny.provider.model.vo.RoomBuildingVo;
import com.csun.cmny.provider.service.*;
import com.csun.cmny.util.DateUtil;
import com.csun.cmny.util.PublicUtil;
import com.csun.cmny.util.RedisKeyUtil;
import com.google.common.collect.Lists;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.Attribute;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Auther: chisj chisj@foxmal.com
 * @Date: 2019-02-19 14:36
 * @Description: the class phone message handler
 */
@Slf4j
@Component
public class PhoneMessageHandler {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private PhoneMessageDecoder phoneMessageDecoder;
    @Resource
    private PhoneMessageEncoder phoneMessageEncoder;
    @Resource
    private DmcPhoneDataService dmcPhoneDataService;
    @Resource
    private DmcPhoneDataHiService dmcPhoneDataHiService;
    @Resource
    private DmcPhoneEventService dmcPhoneEventService;
    @Resource
    private DmcDeviceAlertService dmcDeviceAlertService;
    @Resource
    private DmcPhoneZoneService dmcPhoneZoneService;
    @Resource
    private DmcPhoneEventAlertService dmcPhoneEventAlertService;

    public void handler(ChannelHandlerContext ctx, PhoneMessageDataEntity phoneMsg) {

        try {
            PhoneDataEntity phoneDataEntity = null;
            String redisKey = null;
            String deviceNo = null;
            byte[] data;

            if (phoneMsg == null) {
                return ;
            }

            byte type = phoneMsg.getData()[0];
            switch (type) {
                case PhoneConstant.PHONE_DATA_TYPE_LOGIN_REQ :
                    log.info("===>    phone message: {}", "login");
                    PhoneMessageDataLoginEntity loginMsg = phoneMessageDecoder.parseLogin(phoneMsg);
                    deviceNo = Hex.encodeHexString(loginMsg.getDeviceId()).toUpperCase();
                    redisKey = RedisKeyUtil.getZnPhoneKey(deviceNo);
                    log.info("===>   phone deviceNo: {}", deviceNo);
                    // 1.校验设备是否合法
                    phoneDataEntity = (PhoneDataEntity) redisTemplate.opsForValue().get(redisKey);
                    if (PublicUtil.isNotEmpty(phoneDataEntity)) {
                        PhoneConstant.PHONE_CHANNEL_MAP.put(deviceNo, ctx.channel());
                        ctx.channel().attr(PhoneConstant.PHONE_CHANNEL_KEY).set(phoneDataEntity);
                        // 2.更新设备信息
                        phoneDataEntity.setStatus(DeviceStatusEnum.ONLINE.getKey());
                        phoneDataEntity.setArming((int)loginMsg.getDeviceStatus());
                        redisTemplate.opsForValue().set(redisKey, phoneDataEntity);
                        // 3.回复登录成功
                        data = phoneMessageEncoder.buildLoginAckMsg().getBytes();
                        log.info("===>  phone send data: {}", format(Hex.encodeHexString(data)));
                        ctx.writeAndFlush(Unpooled.copiedBuffer(data));
                        // 4.保存数据
                        savePhoneData(phoneDataEntity);
                    } else {
                        log.info("=====> phone {} not bind", deviceNo);
                    }
                    break;
                case PhoneConstant.PHONE_DATA_TYPE_EVENT :
                    log.info("=====>  phone message: phone event");
                    PhoneMessageDataEventEntity eventMsg = phoneMessageDecoder.parseEvent(phoneMsg);
                    log.info("智能电话机事件数据 ==> eventType = {}, eventMsg = {}",
                            eventMsg.getMsgType(), PhoneEventEnum.getValue(String.format("%#x", eventMsg.getEvent())));
                    // 1.校验设备是否合法
                    phoneDataEntity = ctx.channel().attr(PhoneConstant.PHONE_CHANNEL_KEY).get();
                    if (PublicUtil.isNotEmpty(phoneDataEntity)) {
                        deviceNo = phoneDataEntity.getDeviceNo();
                        log.info("=====> phone deviceNo: {}", deviceNo);
                        redisKey = RedisKeyUtil.getZnPhoneKey(phoneDataEntity.getDeviceNo());
                        // 设备连接
                        phoneDataEntity = (PhoneDataEntity) redisTemplate.opsForValue().get(redisKey);
                        if (PublicUtil.isNotEmpty(phoneDataEntity)) {
                            // 2.更新设备信息
                            PhoneConstant.PHONE_CHANNEL_MAP.put(deviceNo, ctx.channel());
                            phoneDataEntity.setStatus(DeviceStatusEnum.ONLINE.getKey());
                            phoneDataEntity.setArming((int)eventMsg.getDeviceStatus());

                            redisTemplate.opsForValue().set(redisKey, phoneDataEntity);

                            // 回复消息
                            ctx.writeAndFlush(Unpooled.copiedBuffer(PhoneConstant.PHONE_RESPONSE));

                            // 4.保存数据
                            // savePhoneData(phoneDataEntity);

                            // 5.处理报警
                            dealPhoneEvent(phoneDataEntity, eventMsg);
                        } else {
                            log.info("=====> phone {} is be break", deviceNo);
                        }
                    } else {
                        log.info("=====> phone {} not login", deviceNo);
                    }

                    break;
                case PhoneConstant.PHONE_DATA_TYPE_HEART :
                    log.info("=====>  phone message: {}", "phone heart");
                    PhoneMessageDataHeartEntity heartMsg = phoneMessageDecoder.parseHeart(phoneMsg);
                    log.info("智能电话机心跳数据 ==> zoneStatus = [{}], signal = [{}], svtg = [{}], ovtg = [{}], bvtg = [{}]",
                            heartMsg.getZoneStatus(), heartMsg.getSignal(), heartMsg.getSvtg(), heartMsg.getOvtg(), heartMsg.getBvtg());
                    // 1.校验设备是否合法
                    phoneDataEntity = ctx.channel().attr(PhoneConstant.PHONE_CHANNEL_KEY).get();
                    if (PublicUtil.isNotEmpty(phoneDataEntity)) {
                        deviceNo = phoneDataEntity.getDeviceNo();
                        log.info("=====> phone deviceNo: {}", deviceNo);
                        redisKey = RedisKeyUtil.getZnPhoneKey(phoneDataEntity.getDeviceNo());
                        // 设备连接
                        phoneDataEntity = (PhoneDataEntity) redisTemplate.opsForValue().get(redisKey);
                        if (PublicUtil.isNotEmpty(phoneDataEntity)) {
                            // 2.更新设备信息
                            PhoneConstant.PHONE_CHANNEL_MAP.put(deviceNo, ctx.channel());
                            phoneDataEntity.setStatus(DeviceStatusEnum.ONLINE.getKey());
                            phoneDataEntity.setArming((int)heartMsg.getDeviceStatus());
                            phoneDataEntity.setSignal((int)heartMsg.getSignal());
                            phoneDataEntity.setBvtg((float) heartMsg.getBvtg() / 100 + "");
                            phoneDataEntity.setOvtg((float) heartMsg.getOvtg() / 100 + "");
                            phoneDataEntity.setSvtg((float) heartMsg.getSvtg() / 100 + "");

                            redisTemplate.opsForValue().set(redisKey, phoneDataEntity);

                            // 回复消息
                            ctx.writeAndFlush(Unpooled.copiedBuffer(PhoneConstant.PHONE_RESPONSE));

                            // 4.保存数据
                            savePhoneData(phoneDataEntity);
                        } else {
                            log.info("=====> phone {} is be break", deviceNo);
                        }
                    } else {
                        log.info("=====> phone {} not login", deviceNo);
                    }

                    break;
                case PhoneConstant.PHONE_DATA_TYPE_CMD_ACK :
                    log.info("phone cmd ack...");
                    PhoneMessageDataCmdAckEntity cmdAckMsg = phoneMessageDecoder.parseCmdAck(phoneMsg);
                    // 1.校验设备是否合法
                    break;
                default:
                    log.warn("phone message not recognized {} ", type);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Test
    public void main() {

        short ovtg = 485;

        String str = (float)ovtg / 100 + "";

        System.out.println("str = " + str);

    }

    public void dealPhoneEvent(PhoneDataEntity phoneDataEntity, PhoneMessageDataEventEntity eventMsg) {

        DmcPhoneEvent dmcPhoneEvent = new DmcPhoneEvent();
        dmcPhoneEvent.setDeviceId(phoneDataEntity.getDeviceId());
        dmcPhoneEvent.setEventCode(String.format("%#x", eventMsg.getEvent()));
        dmcPhoneEvent.setEventMsg(PhoneEventEnum.getValue(dmcPhoneEvent.getEventCode()));
        dmcPhoneEvent.setEventTime(com.csun.cmny.provider.utils.DateUtil.getDate(eventMsg.getDate()));
        dmcPhoneEvent.setOperator((int)eventMsg.getOperator());
        dmcPhoneEvent.setZoneNo(eventMsg.getZoneNo());

        Integer result = dmcPhoneEventService.save(dmcPhoneEvent);

        DmcPhoneZone dmcPhoneZoneQuery = new DmcPhoneZone();
        dmcPhoneZoneQuery.setDeviceId(phoneDataEntity.getDeviceId());
        dmcPhoneZoneQuery.setZoneNo(dmcPhoneEvent.getZoneNo());
        dmcPhoneZoneQuery = dmcPhoneZoneService.selectOne(dmcPhoneZoneQuery);

        if (PublicUtil.isEmpty(dmcPhoneZoneQuery)) {
            // 没有绑定
            log.warn("====> phoneMac: {}, deviceId: {}, phoneZone: {} not bind.",
                    phoneDataEntity.getDeviceNo(), phoneDataEntity.getDeviceId(), dmcPhoneEvent.getZoneNo());
            return ;
        }

        boolean deal_event_msg = false;
        short event_code = eventMsg.getEvent();
        switch(event_code) {
            // 火警报警
            case PhoneConstant.PHONE_EVENT_FIRE_ALARM :
                //deal_event_msg = true;
                break;
            // 紧急报警
            case PhoneConstant.PHONE_EVENT_EMG_ALARM :
                deal_event_msg = true;
                break;
            // 燃气泄漏
            case PhoneConstant.PHONE_EVENT_NO_ONE_HOME :
                //deal_event_msg = true;
                break;
            default:
                log.warn("event code = {}, event msg = {}", dmcPhoneEvent.getEventCode(), dmcPhoneEvent.getEventMsg());
                break;
        }

        if (!deal_event_msg) {
            // 不用处理
            return ;
        }

        if (PublicUtil.isEmpty(dmcPhoneZoneQuery.getBindType())) {
            // 防区没有绑定, 不用处理
            log.error("电话机防区未绑定 ==> deviceNo = {}, zoneNo = {}",
                    phoneDataEntity.getDeviceNo(), eventMsg.getZoneNo());
            return ;
        }

        Long groupId = null;
        Integer oldmanId = dmcPhoneZoneQuery.getOldmanId();
        String oldmanName = dmcPhoneZoneQuery.getOldmanName();
        String location = dmcPhoneZoneQuery.getLocation();
        OldmanBedRoomBuildingVo obrbv = null;
        RoomBuildingVo rbv = null;
        boolean isSendMsg = false;
        List<String> tagList = Lists.newArrayList();
        switch (dmcPhoneZoneQuery.getBindType()) {
            case PhoneConstant.PHONE_ZONE_BIND_TYPE_CUSTOM :
                isSendMsg = true;
                location = dmcPhoneZoneQuery.getLocation();
                oldmanName = "老人"; // 自定义没有老人姓名
                break;
            case PhoneConstant.PHONE_ZONE_BIND_TYPE_OLDMAN :
                isSendMsg = true;
                obrbv = (OldmanBedRoomBuildingVo) redisTemplate.opsForValue().get(RedisKeyUtil.getOmcOldmanId(dmcPhoneZoneQuery.getOldmanId()));
                if (PublicUtil.isNotEmpty(obrbv)) {
                    oldmanId = obrbv.getOldmanId();
                    oldmanName = obrbv.getOldmanName();
                    groupId = (long) obrbv.getHomeId();
                    location = obrbv.getLocation();
                } else {
                    // 报警器-绑定老人 老人不存在
                    isSendMsg = false;
                    log.error("电话机防区绑定的老人不存在.");
                }
                break;
            case PhoneConstant.PHONE_ZONE_BIND_TYPE_BED :
                isSendMsg = true;
                obrbv = (OldmanBedRoomBuildingVo) redisTemplate.opsForValue().get(RedisKeyUtil.getOmcBedId(dmcPhoneZoneQuery.getBedId()));
                if (PublicUtil.isNotEmpty(obrbv)) {
                    oldmanId = obrbv.getOldmanId();
                    oldmanName = obrbv.getOldmanName();
                    groupId = (long) obrbv.getHomeId();
                    location = obrbv.getLocation();
                } else {
                    // 报警器-绑定床位 床位不存在
                    isSendMsg = false;
                    log.error("电话机防区绑定的床位不存在.");
                }
                break;
            case PhoneConstant.PHONE_ZONE_BIND_TYPE_ROOM :
                isSendMsg = true;
                rbv = (RoomBuildingVo) redisTemplate.opsForValue().get(RedisKeyUtil.getOmcRoomId(dmcPhoneZoneQuery.getRoomId()));
                if (PublicUtil.isNotEmpty(rbv)) {
                    obrbv = new OldmanBedRoomBuildingVo();
                    groupId = (long) rbv.getHomeId();
                    location = rbv.getLocation();
                    BeanUtils.copyProperties(rbv, obrbv);
                } else {
                    // 报警器-绑定房间 房间不存在
                    isSendMsg = false;
                    log.error("电话机防区绑定的房间不存在.");
                }
                break;
        }

        if (isSendMsg) {
            // 添加报警
            DmcDeviceAlert dmcDeviceAlert = new DmcDeviceAlert();
            dmcDeviceAlert.setDeviceId(phoneDataEntity.getDeviceId());
            dmcDeviceAlert.setDeviceNo(phoneDataEntity.getDeviceNo());
            dmcDeviceAlert.setAlertType(DmcDeviceAlertTypeEnum.PHONE_BTN_ALERT.getKey());
            dmcDeviceAlert.setGroupId(groupId);
            dmcDeviceAlert.setOldmanId(oldmanId);
            if (PublicUtil.isEmpty(oldmanName)) {
                oldmanName = "老人";
            }
            dmcDeviceAlert.setOldmanName(oldmanName);
            dmcDeviceAlert.setLocation(location);
            dmcDeviceAlert.setGroupId(phoneDataEntity.getGroupId());
            String alertInfo = String.format("[%s]的[%s]使用报警器按钮进行了呼叫, 请前往处理!",
                    dmcDeviceAlert.getLocation(), dmcDeviceAlert.getOldmanName());
            dmcDeviceAlert.setAlertInfo(alertInfo);
            dmcDeviceAlertService.save(dmcDeviceAlert);

            // 下发报警
            dmcDeviceAlertService.sendDeviceAlertMsg(dmcDeviceAlert, obrbv);

            // 添加
            DmcPhoneEventAlert dmcPhoneEventAlert = new DmcPhoneEventAlert();
            dmcPhoneEventAlert.setEventId(dmcPhoneEvent.getId());
            dmcPhoneEventAlert.setAlertId(dmcDeviceAlert.getId());
            dmcPhoneEventAlertService.save(dmcPhoneEventAlert);
        }
    }

    public void savePhoneData(PhoneDataEntity phoneDataEntity) {

        DmcPhoneData dmcPhoneData = new DmcPhoneData();
        DmcPhoneDataHi dmcPhoneDataHi = new DmcPhoneDataHi();

        BeanUtils.copyProperties(phoneDataEntity, dmcPhoneData);
        BeanUtils.copyProperties(phoneDataEntity, dmcPhoneDataHi);

        // dmcPhoneDataService.updatePhoneDataByDeviceId(dmcPhoneData);
        dmcPhoneDataService.update(dmcPhoneData);
        dmcPhoneDataHiService.save(dmcPhoneDataHi);
    }

    public String format(String str) {

        String regex = "(.{2})";
        str = str.replaceAll(regex, "$1 ");
        return str;
    }

    /**
     * 端开设备连接
     *
     * @param deviceNo
     */
    public void clear(String deviceNo) {

        Channel channel = PhoneConstant.PHONE_CHANNEL_MAP.get(deviceNo);

        channel.close();
    }
}
